package nl.ViewerServer.ServerManager.DataManager.DataObjects;

import java.awt.Dimension;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileReader;
import java.io.IOException;

import nl.ViewerServer.Exceptions.DataInitializationException;
import nl.ViewerServer.Exceptions.DataObjectException;

import org.apache.log4j.Logger;

public class ZoomifyDataObject extends DataObject {
   private static Logger log = Logger.getLogger( ZoomifyDataObject.class );

   private transient int tileFolderCount;
   
   public ZoomifyDataObject( String dataType ) {
      super( dataType );
   }

   public ZoomifyDataObject( int dataObjectId, int lookmarkId, String location, String dataType ) {
      super( dataObjectId, lookmarkId, location, dataType );
   }

   private int getImageProperty( String line, String property ) {
      // find the <property>="
      int startIndex = line.toUpperCase().indexOf( property.toUpperCase() + "=\"" );
      if( startIndex < 0 )
         return -1;

      // skip the name of the property and the ="
      startIndex += property.length() + 2;

      // find the closing "
      int endIndex = line.indexOf( "\"", startIndex );
      if( endIndex < 0 )
         return -1;

      if( endIndex - 1 > startIndex ) { // we want at least 1 digit
         try {
            String propertyValue = line.substring( startIndex, endIndex );

            return Integer.parseInt( propertyValue );
         }
         catch( NumberFormatException nfe ) {
            // ignore for now that there was no property value of type integer
         }
      }
      return -1;
   }

   public boolean initialize() throws DataInitializationException {
      // load the imageProperties.xml
      // <IMAGE_PROPERTIES WIDTH="695" HEIGHT="630" NUMTILES="14" NUMIMAGES="1" VERSION="1.8" TILESIZE="256" />
      String line = null;
      try {
         File fLocation = new File( location + "/output/ImageProperties.xml" ); // default location after zoomify
         if( fLocation.exists() == true ) {
            location = location + "/output";
         } else {
            fLocation = new File( location + "/ImageProperties.xml" ); // last try
         }

         BufferedReader br = new BufferedReader( new FileReader( fLocation ) );
         line = br.readLine();

         if( line == null )
            throw new Exception( "The file could be opened but no data was available" );
      }
      catch( Exception e ) {
         throw new DataInitializationException(
               "Zoomify property file ImageProperties.xml could not be retrieved from " + location, e );
      }

      // parse the line
      int width = getImageProperty( line, "WIDTH" );
      int height = getImageProperty( line, "HEIGHT" );
      int numtiles = getImageProperty( line, "NUMTILES" );
      
      this.tileFolderCount = ( ( numtiles - 1 ) / 256 ) + 1;
      
      int tilesize = getImageProperty( line, "TILESIZE" );

      int imageWidth = width;
      int imageHeight = height;

      this.setBounds( new Bounds( width, height, 1 ) );
      this.tilesize = tilesize;

      log.debug( "Zoomify tile: width = " + width + "/height = " + height + "/tilecount = " + numtiles + "/tilesize="
         + tilesize );
      // build the pyramid structure
      int max = Math.max( width, height );

      int tierCount = 1;
      while( max > tilesize ) {
         max /= 2;
         tierCount++;
      }

      // initialize the tiers
      resolutionParts = new Dimension[ tierCount ];

      int tierHeight = tierCount - 1;
      while( tierHeight >= 0 ) {
         // calculate the amount of tiles for a given tier
         resolutionParts[tierHeight] = new Dimension();
         resolutionParts[tierHeight].width = (int)Math.ceil( (double)imageWidth / (double)tilesize );
         resolutionParts[tierHeight].height = (int)Math.ceil( (double)imageHeight / (double)tilesize );
         log.debug( "Zoomify tier: " + tierHeight + ": " + resolutionParts[tierHeight].width + ", "
            + resolutionParts[tierHeight].height );

         imageWidth /= 2;
         imageHeight /= 2;
         tierHeight--;
      }

      log.info( "ZoomifyDataObject initialized" );

      return true;
   }
   
   private int getTileGroupNumber2( String location, int resolution, int xPos, int yPos ) {
      for( int G = 0; G < this.tileFolderCount; G++ ) {
         if( new File( location + "/TileGroup" + G + "/" + resolution + "-" + xPos + "-" + yPos + ".jpg" ).exists() ) return G;
      }
      return -1;
   }

   
   
   protected int getTileGroupNumber( int resolution, int xPos, int yPos ) {
      /*
       * Method below doesn't seem to work in all cases 
       */
      
//      int G = 0;
//      int tileCountForGroup = 0;
//      
//      for( int r = 0; r < resolution; r++ ) {
//         for( int y = 0; y < resolutionParts[r].height; y++ ) {
//            for( int x = 0; x < resolutionParts[r].width; x++ ) {
//
//               tileCountForGroup++;
//               if( tileCountForGroup == 256 ) {
//                  G++;
//                  tileCountForGroup = 0;
//               }
//
//               if( r == resolution && xPos == x && yPos == y )
//                  return G;
//            }
//         }
//      }
//      return G;
      return -1;
   }

   public byte[] getPart( int resolution, int x, int y ) throws DataObjectException, IOException {
      if( resolution >= resolutionParts.length ) {
         throw new DataObjectException( "Resolution " + resolution + " is not available for this data object: " + this );
      }

      Dimension p = resolutionParts[resolution];
      if( x < 0 || y < 0 || x >= p.width || y >= p.height ) {
         throw new DataObjectException( "Part " + x + ", " + y + " is not available for this resolution (" + resolution
            + ") of data object: " + this );
      }

      // locate the correct file and read it

      // first find the folder containing the tile (there are 256 tiles per folder): TileGroup0, TileGroup1, etc
      //int G = getTileGroupNumber( resolution, x, y );
      int G = getTileGroupNumber2( location, resolution, x, y );

      String subfolder = "TileGroup" + G;

      File f = new File( location + "/" + subfolder + "/" + resolution + "-" + x + "-" + y + ".jpg" );
      if( !f.isFile() )
         throw new DataObjectException( "Zoomify data for " + resolution + "-" + x + "-" + y + " in TileGroup " + G
            + " was not found in " + f.getAbsolutePath() );

      FileInputStream fr = new FileInputStream( f );
      byte[] data = new byte[ (int)f.length() ];
      fr.read( data, 0, (int)f.length() );

      fr.close();
      log.debug( "getPart returned " + resolution + "-" + x + "-" + y + " in TileGroup " + G + ": " + data.length
         + " bytes" );
      return data;
   }

}
